/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2007 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using G=Gtk;
using Gtk;
using Glade;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;
using Galaxium.Gui;

namespace Galaxium.Gui.GtkGui
{
	public abstract class BasicAccountWidget : AbstractAccountWidget
	{
		protected Table _account_table = null;
		protected Entry _password_entry = null;
		protected CheckButton _autoconnect_check = null;
		protected CheckButton _rememberpass_check = null;
		protected Button _connect_button = null;
		protected Button _cancel_button = null;
		protected ProgressBar _progress_bar = null;
		protected Button _remove_button = null;
		protected Button _settings_button = null;
		protected ImageComboBox<IPresence> _presence_combo = null;
		
		private IPresence _initial_presence;
		
		public BasicAccountWidget (IProtocol protocol) : base (protocol)
		{
			_account_table = new G.Table (3, 3, false);
			_account_table.RowSpacing = 3;
			_account_table.ColumnSpacing = 3;
			_account_table.WidthRequest = 100;
			
			_remove_button = new G.Button ();
			_remove_button.Label = String.Empty;
			_remove_button.Image = new G.Image (IconUtility.GetIcon ("galaxium-remove", IconSizes.Small));
			_remove_button.Clicked += RemoveButtonClicked;
			
			_settings_button = new G.Button ();
			_settings_button.Label = String.Empty;
			_settings_button.Image = new G.Image (IconUtility.GetIcon ("galaxium-preferences", IconSizes.Small));
			_settings_button.Clicked += SettingsButtonClicked;
			
			_password_entry = new G.Entry ();
			_password_entry.Visibility = false;
			_password_entry.Changed += PasswordChanged;
			
			_connect_button = new Button ();
			_connect_button.Label = "Connect";
			_connect_button.Image = new Image (IconUtility.GetIcon ("galaxium-ok", IconSizes.Small));
			_connect_button.Clicked += ConnectButtonClicked;
			
			_cancel_button = new G.Button ();
			_cancel_button.Label = "Cancel";
			_cancel_button.Image = new Image (IconUtility.GetIcon ("galaxium-cancel", IconSizes.Small));
			_cancel_button.Clicked += CancelButtonClicked;
			
			_autoconnect_check = new G.CheckButton ("Automatically Connect");
			_rememberpass_check = new G.CheckButton ("Remember Password");
			
			_progress_bar = new G.ProgressBar();
			_progress_bar.Text = " ";
		}
		
		public override void Initialize ()
		{
			base.Initialize ();
			
			Label account_label = new Label ("Account:");
			_layout_widget = account_label;
			account_label.Xalign = 1.0f;
			
			// Setup the account combo
			_account_combo = CreateAccountCombo ();
			
			HBox account_box = new G.HBox ();
			account_box.PackStart (_account_combo, true, true, 0);
			account_box.PackStart (_settings_button, false, true, 0);
			account_box.PackStart (_remove_button, false, true, 0);
			
			Label pass_label = new G.Label ("Password:");
			pass_label.Xalign = 1.0f;
			
			Label presence_label = new G.Label ("Presence:");
			presence_label.Xalign = 1.0f;
			
			// Attach everything to the table
			_account_table.Attach (account_label, 0, 1, 0, 1, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_account_table.Attach (account_box, 1, 3, 0, 1, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (pass_label, 0, 1, 1, 2, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_password_entry, 1, 3, 1, 2, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (presence_label, 0, 1, 2, 3, AttachOptions.Fill, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_presence_combo, 1, 3, 2, 3, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_connect_button, 1, 2, 3, 4, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_cancel_button, 2, 3, 3, 4, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_rememberpass_check, 1, 3, 4, 5, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			_account_table.Attach (_autoconnect_check, 1, 3, 5, 6, AttachOptions.Expand | AttachOptions.Fill | AttachOptions.Shrink, AttachOptions.Fill, 0, 0);
			
			_account_combo.FocusChain = new G.Widget[] { _account_combo.Entry };
			
			account_box.FocusChain = new G.Widget[] { _account_combo };
			
			_account_table.FocusChain = new Widget[] { 
				account_box, _password_entry, _presence_combo, _connect_button, _cancel_button,
				_rememberpass_check, _autoconnect_check };
			
			SetAccountWidget (AccountWidgetPositions.Content, _account_table, true, true, 0);
			SetAccountWidget (AccountWidgetPositions.Progress, _progress_bar, true, true, 0);
			
			base.PopulateFields ();
			
			_native_widget.ShowAll ();
		}
		
		public override void Connect ()
		{
			// This is mostly to allow auto-connect to start the process
			ConnectButtonClicked (this, new EventArgs ());
		}
		
		protected virtual void LoadAccountInfo ()
		{
			IAccount account = AccountUtility.GetAccount(_protocol, _account_combo.Entry.Text);
			
			// Here we load all the basic account details into the UI.
			
			if (account != null)
			{
				_password_entry.Text = account.Password;
				_autoconnect_check.Active = account.AutoConnect;
				
				if (account.InitialPresence != null)
					_presence_combo.Select (account.InitialPresence);
				
				_rememberpass_check.Active = account.RememberPassword;
			}
			else
			{
				_password_entry.Text = String.Empty;
				_autoconnect_check.Active = false;
				_rememberpass_check.Active = false;
				_presence_combo.SelectFirst ();
			}
			
			Account = account;
		}
		
		protected IPresence GetInitialPresence ()
		{
			if (_initial_presence == null)
			{
				TreeIter iter;
				
				if (_presence_combo.GetActiveIter (out iter))
					_initial_presence = _presence_combo.Model.GetValue (iter, 0) as IPresence;
				else
					_initial_presence = UnknownPresence.Instance;
			}
			
			return _initial_presence;
		}
		
		protected override void UpdateProgress (string message, double percent)
		{
			// This will stop a disconnect from overwriting an error message
			// I'm not sure it's the best way to handle it though
			if ((_progress_bar.Fraction == 0) && (percent == 0) &&
			    (!(string.IsNullOrEmpty (_progress_bar.Text) || string.IsNullOrEmpty (_progress_bar.Text.Trim ()))))
				return;
			
			_progress_bar.Text = message;
			_progress_bar.Fraction = percent;
		}
		
		protected override IAccount SetAccount ()
		{
			Log.Warn ("Protocol account widget does not properly override IAccount SetAccount ()!");
			
			return null;
		}
		
		public override void EnableFields ()
		{
			OnEnableChange (EventArgs.Empty);
			
			if (_account_combo.Entry.Text.Length < 1)
				_connect_button.Sensitive = false;
			else
				_connect_button.Sensitive = true;
			
			if (SessionUtility.Count >= 1)
				_cancel_button.Sensitive = true;
			else
				_cancel_button.Sensitive = false;
			
			_rememberpass_check.Sensitive = true;
			_autoconnect_check.Sensitive = true;
			_password_entry.Sensitive = true;
			_account_combo.Sensitive = true;
			_presence_combo.Sensitive = true;
			_remove_button.Sensitive = true;
			_settings_button.Sensitive = true;
		}
		
		public override void DisableFields (bool omit_cancel)
		{
			OnDisableChange (EventArgs.Empty);
			
			_connect_button.Sensitive = false;
			_cancel_button.Sensitive = omit_cancel;
			
			_rememberpass_check.Sensitive = false;
			_autoconnect_check.Sensitive = false;
			_password_entry.Sensitive = false;
			_account_combo.Sensitive = false;
			_presence_combo.Sensitive = false;
			_remove_button.Sensitive = false;
			_settings_button.Sensitive = false;
		}
		
		protected virtual bool CheckInput ()
		{
			if (_password_entry.Text.Length == 0 || _account_combo.Entry.Text.Length == 0)
				return false;
			
			return true;
		}
		
		protected abstract void SettingsButtonClicked (object sender, EventArgs args);
		
		protected virtual void ConnectButtonClicked (object sender, EventArgs args)
		{
			if (_current_session == null)
			{
				Log.Warn ("Protocol account widget failed to create a usable session!");
				return;
			}
			
			_current_session.Disconnected += SessionDisconnected;
			
			DisableFields (true);
			
			_connecting = true;
			
			_current_session.Connect ();
		}
		
		protected virtual void CancelButtonClicked (object sender, EventArgs args)
		{
			if (_current_session != null && _connecting)
				_current_session.Disconnect ();
			
			CancelLogin ();
		}
		
		protected virtual void RemoveButtonClicked (object sender, EventArgs args)
		{
			RemoveSelectedAccount ();
		}
		
		protected virtual void RememberPasswordToggled (object sender, EventArgs args)
		{
			
		}
		
		protected virtual void AutoConnectToggled (object sender, EventArgs args)
		{
			
		}
		
		protected virtual void PasswordChanged (object sender, EventArgs args)
		{
			_connect_button.Sensitive = CheckInput ();
		}
		
		protected override void AccountNameChanged (object sender, EventArgs args)
		{
			LoadAccountInfo ();
			
			_connect_button.Sensitive = CheckInput ();
		}
		
		protected override void SessionDisconnected (object sender, SessionEventArgs args)
		{
			EnableFields ();
			
			base.SessionDisconnected (sender, args);
		}
		
		protected override void ErrorOccurred (string error)
		{
			UpdateProgress (error, 0);
			
			EnableFields ();
		}
	}
}